#ifndef __BUILDIN_EVENTS__
#define __BUILDIN_EVENTS__

namespace mm
{
	static size_t query_block( void * base )
	{
		uint8_t * AllocationBase = (uint8_t*)base;
		MEMORY_BASIC_INFORMATION mbi = {};
		for(;;)
		{
			if( !VirtualQuery( AllocationBase, &mbi, sizeof(mbi) ) )
				break;

			if( !mbi.AllocationBase )
				break;

			if( mbi.AllocationBase == base )
				AllocationBase += mbi.RegionSize;
			else
				break;
		}
		size_t bytes = AllocationBase - (uint8_t*)base;
		return bytes ? bytes : 0x1000;
	}

	static void clear_import_table( void *base )
	{
		if( IsBadReadPtr(base, sizeof(IMAGE_DOS_HEADER) ) )
			return;

		IMAGE_DOS_HEADER * dh = (IMAGE_DOS_HEADER*)base;
		if( dh->e_magic != IMAGE_DOS_SIGNATURE )
			return ;

		IMAGE_NT_HEADERS32 * nh32 = (IMAGE_NT_HEADERS32 *)( (char*)dh + dh->e_lfanew );
		if( IsBadReadPtr(nh32, sizeof(IMAGE_NT_HEADERS32) ) )
			return ;

		if( nh32->Signature != IMAGE_NT_SIGNATURE )
			return ;

		IMAGE_DATA_DIRECTORY * dde = 0;
		if( nh32->FileHeader.Machine == 0x8664 )
		{
			IMAGE_NT_HEADERS64 * nh64 = (IMAGE_NT_HEADERS64 *)nh32;
			dde = &(nh64->OptionalHeader.DataDirectory[IMAGE_DIRECTORY_ENTRY_IMPORT]);
		}
		else
		{
			dde = &(nh32->OptionalHeader.DataDirectory[IMAGE_DIRECTORY_ENTRY_IMPORT]);
		}
		
		char szmod[256] = {};
		GetModuleFileNameA( (HMODULE)base, szmod, sizeof(szmod) );
		printf( "%s\n", szmod );

		DWORD oldProt = 0;
		if( VirtualProtect( dde, sizeof(IMAGE_DATA_DIRECTORY), PAGE_READWRITE, &oldProt ) )
		{
			dde->Size = 0;
			dde->VirtualAddress = 0;
			VirtualProtect( dde, sizeof(IMAGE_DATA_DIRECTORY), oldProt, &oldProt );

			throw_message( "CleanImportTable : %s", szmod );
		}
		else
		{
			DWORD ERR = GetLastError();
			int a =1;
		}
	}

	static void enum_module()
	{
		MEMORY_BASIC_INFORMATION mbi = {};
		uint8_t * pBase = NULL;
		while( VirtualQuery( pBase, &mbi, sizeof(mbi) ) )
		{
			if( mbi.Type == MEM_FREE || !mbi.Type )
			{
				pBase += mbi.RegionSize;
			}
			else
			{
				size_t cb = query_block( mbi.AllocationBase );
				clear_import_table( pBase );
				pBase += cb;
			}
		}
	}


	struct dll_watcher : mm::basic_watcher_t 
	{
		typedef std::set< refp<basic_watcher_t> > dll_watcher_set_t;

		dll_watcher_set_t	_dllwatchers;
		mutex_t				_lock;

		dll_watcher()  : mm::basic_watcher_t(0)
		{
			mm::wpm_attach( LdrLoadDll, this, LdrLoadDll, true, 4 * 4 );
		}

		long bind_watcher( basic_watcher_t * wp )
		{
			sync_t __sync__(_lock);
			dll_watcher_set_t::_Pairib ib = _dllwatchers.insert( wp );
			if( !ib.second ) return E_FAIL;
			active_one_watcher( wp );
			return S_OK;
		}

	protected:

		virtual size_t impl_before_call( size_t uid, hk::exec_context * pctx, size_t deny_count )
		{
			if( deny_count ) return false;
			sync_t __sync__(_lock);
			if( uid == (size_t)LdrLoadDll )
			{
				PWSTR SearchPath = (PWSTR)pctx->args[0];
				PULONG DllCharacteristics = (PULONG)pctx->args[1];
				PUNICODE_STRING DllName = (PUNICODE_STRING)pctx->args[2];
				if( !DllName || !DllName->Buffer || !DllName->Length ) return true;
				HMODULE BaseNow = 0;
				NTSTATUS nts = LdrGetDllHandle( SearchPath, DllCharacteristics?*DllCharacteristics:0, DllName, &BaseNow );
				if( nts < 0 || BaseNow == NULL )
					set_thread_data( "$dllmon", xv::xvalue_t(tcs::ws_to_as(DllName->Buffer)) );
			}
			return true;
		}
		////////////////////////////////////////////////////////////////////////////
		virtual long impl_after_call( size_t uid, hk::exec_context * pctx )
		{
			NTSTATUS nts = (NTSTATUS)pctx->retval();
			if( nts < 0 ) return NORMAL_LEAVE;
			if( uid == (size_t)LdrLoadDll )
			{
				xv::xvalue_t f_new = get_thread_data( "$dllmon" );
				if( !f_new.isMBS() ) return NORMAL_LEAVE;
				PVOID * BaseAddress = (PVOID*)pctx->args[3];
				if( !BaseAddress ) return NORMAL_LEAVE;
				const char * dllname = f_new;
				dispatch_dll_load( dllname, *BaseAddress );
			}
			return NORMAL_LEAVE;
		}

		void dispatch_dll_load( const char * dllname, void * base )
		{
			sync_t __sync__(_lock);

			clear_import_table( base );

			size_t succ = 0;
			dll_watcher_set_t::iterator it = _dllwatchers.begin();
			for( ; it != _dllwatchers.end(); ++ it )
			{
				refp<basic_watcher_t> bw = *it;
				succ += active_one_watcher( bw );
			}

			if( succ ) wpm_banch_active(true);
		}

		size_t active_one_watcher( basic_watcher_t * bw )
		{
			size_t succ = 0;
			dllwp_inf_t inf;
			for( size_t i = 0; bw->get_item(i,inf); ++ i )
			{
				if( inf.status->dllbase )
					continue;

				HMODULE hmod = GetModuleHandleA( inf.item->dllname );
				if( !hmod ) continue;

				inf.status->dllbase = hmod;

				void * pfn = GetProcAddress( hmod, inf.item->api );
				if( !pfn ) continue;

				inf.status->dllexp = pfn;

				throw_message( "hooking %s!%s", inf.item->dllname, inf.item->api );

				long lr = wpm_attach( pfn, bw, (size_t)(inf.item), true, inf.item->retn );

				if( lr >= 0 ) succ ++;
			}
			return succ;
		}

	};





};


#endif